package java_thinking.unit_21.concurrentutil;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Desc:   各种栅栏
 *      1.CyclicBarrier：
 *         1 内部Generation描述着CyclicBarrier的更新换代。在CyclicBarrier中，同一批线程属于同一代。当有parties个线程到达
 *           barrier之后，generation就会被更新换代。其中 broken 标识该当前CyclicBarrier是否已经处于中断状态。
 *         2 dowait(boolean, long)方法线程将处于等待状态，除非发生以下情况：
 *           2.1 最后一个线程到达，即index == 0
 *           2.2 某个参与线程等待超时
 *           2.3 某个参与线程被中断
 *           2.4 调用了CyclicBarrier的reset()方法。该方法会将屏障重置为初始状态
 *         3 总结CyclicBarrier的await方法实现，假设线程thread1和线程thread2都执行到CyclicBarrier的await()，都进入
 *           dowait(boolean timed, long nanos)，thread1先获取到独占锁，执行到--count的时，index等于1，所以进入下面的for
 *           循环，接着执行trip.await()，进入await()方法，执行Node node = addConditionWaiter()将当前线程构造成Node节点并
 *           加入到Condition等待队列中，然后释放获取到的独占锁，当前线程进入阻塞状态；此时，线程thread2可以获取独占锁，继续
 *           执行--count，index等于0，所以先执行command.run()，输出myThread，然后执行nextGeneration()，nextGeneration()
 *           中trip.signalAll()只是将Condition等待队列中的Node节点按之前顺序都转移到了AQS同步队列中，这里也就是将thread1对
 *           应的Node节点转移到了AQS同步队列中，thread2执行完nextGeneration()，返回return 0之前，细看代码还需要执行
 *           lock.unlock()，这里会执行到ReentrantLock的unlock()方法，最终执行到AQS的unparkSuccessor(Node node)方法，从
 *           AQS同步队列中的头结点开始释放节点，唤醒节点对应的线程，即thread1恢复执行。
 *
 *           如果有三个线程thread1、thread2和thread3，假设线程执行顺序是thread1、thread2、thread3，那么thread1、thread2
 *           对应的Node节点会被加入到Condition等待队列中，当thread3执行的时候，会将thread1、thread2对应的Node节点按
 *           thread1、thread2顺序转移到AQS同步队列中，thread3执行lock.unlock()的时候，会先唤醒thread1，thread1恢复继续执
 *           行，thread1执行到lock.unlock()的时候会唤醒thread2恢复执行。
 *
 *        4. CyclicBarrier当所有线程都到达屏障点后，等待线程的执行顺序是什么样的？
 *           CyclicBarrier的await方法是使用ReentrantLock和Condition控制实现的，使用的Condition实现类是ConditionObject，
 *           它里面有一个等待队列和await方法，这个await方法会向队列中加入元素。当调用CyclicBarrier的await方法会间接调用
 *           ConditionObject的await方法，当屏障冲破后首先执行指定的barrierAction，然后依次唤醒condition队列中的阻塞等待
 *           的线程，将他们转移到AQS的同步队列，等待主线程释放锁资源，当主线程释放后，先后执行同步队列等待锁的线程。
 *
 *     2.CountDownLatch：
 *          1 使用CountDownLatch(int count)构建CountDownLatch实例，将count参数赋值给内部计数器state，调用await()方法阻塞
 *            当前线程，并将当前线程封装加入到等待队列中，直到state等于零或当前线程被中断；调用countDown()方法使state值减一
 *            ，如果state等于零则唤醒等待队列中的线程。
 *
 *          2 实战经验：
 *            实际工作中，CountDownLatch适用于如下使用场景： 客户端的一个同步请求查询用户的风险等级，服务端收到请求后会请求
 *            多个子系统获取数据，然后使用风险评估规则模型进行风险评估。如果使用单线程去完成这些操作，这个同步请求超时的可能
 *            性会很大，因为服务端请求多个子系统是依次排队的，请求子系统获取数据的时间是线性累加的。此时可以使用
 *            CountDownLatch，让多个线程并发请求多个子系统，当获取到多个子系统数据之后，再进行风险评估，这样请求子系统获取数
 *            据的时间就等于最耗时的那个请求的时间，可以大大减少处理时间。
 *
 *          3 面试考点
 *            CountDownLatch和CyclicBarrier的异同？
 *            相同点：都可以实现线程间的等待。 不同点： 1.侧重点不同，CountDownLatch一般用于一个线程等待一组其它线程；而
 *            CyclicBarrier一般是一组线程间的相互等待至某同步点； 2.CyclicBarrier的计数器是可以重用的，而CountDownLatch
 *            不可以。
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 * @author: cww
 * @DateTime: 2019-07-13 20:25
 */

public class BarrierTheory {
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    static boolean end = false;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                test();

            }
        });
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("334");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                condition.signal();
                end = true;
            }
        });
        thread.start();
        thread1.start();

    }

    public static void test() {
        lock.lock();
        try {
            for (;;) {
                    try {
                        System.out.println("await");
                        condition.await();
                        if (end) {
                            System.out.println("return");
                            return;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        } finally {
            lock.unlock();
        }
    }
}
